package com.google.code.jholidays.events;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.util.Calendar;
import java.util.Date;

import org.junit.Test;

import com.google.code.jholidays.core.EventDescriptor;
import com.google.code.jholidays.events.parameters.DateParameters;
import com.google.code.jholidays.events.parameters.DayOfWeekOrder;
import com.google.code.jholidays.events.properties.FloatingEventProperties;

public class FloatingEventTest {

    @Test(expected = IllegalArgumentException.class)
    public void testCreateNullProperties() {
	FloatingEvent event = new FloatingEvent(null);
	assertNull(event);
    }

    @Test
    public void testFloatingEvent() {
	int eventID = 2;

	EventDescriptor descriptor = new EventDescriptor(eventID);

	DateParameters params = new DateParameters(1, Calendar.getInstance()
		.getActualMinimum(Calendar.MONTH), DayOfWeekOrder.First);

	FloatingEventProperties props = new FloatingEventProperties(descriptor,
		params);
	assertNotNull(props);

	FloatingEvent event = new FloatingEvent(props);
	assertNotNull(event);
    }

    @Test
    public void testGetDate() {
	int eventID = 2;

	EventDescriptor descriptor = new EventDescriptor(eventID);

	DateParameters params = new DateParameters(1, Calendar.getInstance()
		.getActualMinimum(Calendar.MONTH), DayOfWeekOrder.First);
	assertNotNull(params);

	FloatingEventProperties props = new FloatingEventProperties(descriptor,
		params);
	assertNotNull(props);

	FloatingEvent event = new FloatingEvent(props);
	assertNotNull(event);

	final int year = 2009;

	// System.out.println(calculateDate(params, year));
	// System.out.println(event.getDate(year));

	assertEquals(calculateDate(params, year), event.getDate(year));
    }

    @Test
    public void testGetDateFirst2FourthOrder() {
	int eventID = 2;

	for (int i = 2000; i < 3000; ++i) {
	    int loMonth = Calendar.getInstance().getActualMinimum(
		    Calendar.MONTH);
	    int hiMonth = Calendar.getInstance().getActualMaximum(
		    Calendar.MONTH);

	    for (int j = loMonth; j <= hiMonth; ++j) {

		int loDay = Calendar.getInstance().getActualMinimum(
			Calendar.DAY_OF_WEEK);
		int hiDay = Calendar.getInstance().getActualMaximum(
			Calendar.DAY_OF_WEEK);

		for (int k = loDay; k <= hiDay; ++k) {
		    EventDescriptor descriptor = new EventDescriptor(eventID);

		    DateParameters params = new DateParameters(k, j,
			    DayOfWeekOrder.First);
		    assertNotNull(params);

		    FloatingEventProperties props = new FloatingEventProperties(
			    descriptor, params);
		    assertNotNull(props);

		    FloatingEvent event = new FloatingEvent(props);
		    assertNotNull(event);

		    final int year = i;

		    assertEquals(calculateDate(params, year), event
			    .getDate(year));
		}

	    }

	}

    }

    @Test
    public void testGetDateParameters() {
	int eventID = 2;

	EventDescriptor descriptor = new EventDescriptor(eventID);

	DateParameters params = new DateParameters(1, Calendar.getInstance()
		.getActualMinimum(Calendar.MONTH), DayOfWeekOrder.First);

	FloatingEventProperties props = new FloatingEventProperties(descriptor,
		params);
	assertNotNull(props);

	FloatingEvent event = new FloatingEvent(props);
	assertNotNull(event);

	assertSame(params, event.getDateParameters());
    }

    protected Date calculateDate(DateParameters params, int year) {
	Date result = null;

	assertNotNull(params);

	Calendar c = Calendar.getInstance();
	c.clear();

	int orderIndex = getOrderIndex(year, params.getMonth(), params
		.getDayOfWeek(), params.getOrder());
	assertTrue(orderIndex != -1);

	c.set(year, params.getMonth(), 1);
	c.set(Calendar.DAY_OF_WEEK, params.getDayOfWeek());
	c.set(Calendar.DAY_OF_WEEK_IN_MONTH, orderIndex);

	result = c.getTime();
	return result;
    }

    private int getOrderIndex(int year, int month, int dayOfWeek,
	    DayOfWeekOrder order) {
	int result = -1;

	switch (order) {
	case First:
	    result = 1;
	    break;
	case Second:
	    result = 2;
	    break;
	case Third:
	    result = 3;
	    break;
	case Fourth:
	    result = 4;
	    break;
	case Last:
	    result = getLastOrderInMonth(year, month, dayOfWeek);
	    break;
	default:
	    break;
	}

	return result;
    }

    private int getLastOrderInMonth(int year, int month, int dayOfWeek) {
	int result = -1;

	try {
	    Calendar c = Calendar.getInstance();
	    c.clear();
	    c.set(year, month, 1);
	    c.set(Calendar.DAY_OF_WEEK, dayOfWeek);

	    result = c.getActualMaximum(Calendar.DAY_OF_WEEK_IN_MONTH);
	} catch (Exception e) {
	    // nothing todo here
	}

	return result;
    }

}
